Alternatywne miary ryzyka

  • Miara ryzyka powinna się skupiać na ujemnych zwrotach, np. największym spadku (ang. Maximum Drawdown) lub współczynniku Sortino.

Alternatywne miary ryzyka

Współczynnik Sharpe’a

\[ \small{ Sharpe = (R_p-R_f)\cdot{\sigma_j}^{-1}, \\ Sharpe - \text{współczynnik Sharpe'a}, \\ R_p - \text{zannualizowana średnia stopa zwrotu}, \\ R_f - \text{stopa zwrotu wolna od ryzyka}, \\ \sigma_j - \text{odchylenie standardowe portfela}.} \]

Współczynnik Sortino

\[ \small{ Sortino = (R_j-R_f)\cdot{\sigma_d}^{-1}, \\ Sortino - \text{współczynnik Sortino}, \\ R_j - \text{zannualizowana średnia stopa zwrotu}, \\ R_f - \text{stopa zwrotu wolna od ryzyka}, \\ \sigma_d = \sqrt{\frac{1}{\sum_{i=1}^{n} \mathbf{1}_{\{R_i - R_f < 0\}}}\sum_{i=1}^{n} \min(0, R_i - R_f)^2} - \\ -\text{ odchylenie standardowe ujemnych zwrotów}. } \]

Współczynnik Sortino

GOOGL
Date
2006-01-03 217.62
2006-01-04 222.62
2006-01-05 225.62
2006-01-06 232.83
2006-01-09 233.45
... ...
2015-12-24 765.84
2015-12-28 782.24
2015-12-29 793.96
2015-12-30 790.30
2015-12-31 778.01

2517 rows × 1 columns

Wskaźnik Sortino dla akcji Google

rfr = 0.04
target_return = 0

returns = data.pct_change()
negative_returns = returns.loc[returns['GOOGL'] < target_return]
expected_return = returns.mean()
annualized_return = (expected_return+1)**252 - 1
down_std = negative_returns.std()

sortino_ratio = (annualized_return - rfr) / (down_std*np.sqrt(252))

Sortino dla akcji Google wynosi ok. 0.702.

Sharpe dla akcji Google wynosi ok. 0.488.

Alternatywne miary ryzyka

Największy spadek (ang. Maximum Drawdown)

  • Największa procentowa strata od szczytu do dna.

  • Zależy od wybranego okna czasowego.

Największy spadek (ang. Maximum Drawdown)

# Maksymalne spadki
roll_max = data.rolling(center=False, min_periods=1, window=252).max()

# Dzienny spadek w stosunku do maksimum
daily_draw_down = data/roll_max - 1.0

# Maksymalny dzienny spadek
max_daily_draw_down = daily_draw_down.rolling(center=False, min_periods=1, window=252).min()

# Wykres
plt.figure(figsize=(15,8))
plt.plot(data.index, daily_draw_down, label='Dzienne spadki')
plt.plot(data.index, max_daily_draw_down, label='Maksymalne spadki w okresie 252 dni')
plt.legend()
plt.show()

Pyfolio

Pyfolio

Dane

positions = pd.read_csv('data/test_pos.csv', index_col=0)
positions.index = pd.to_datetime(positions.index)

benchmark = pd.read_csv('data/sp500.csv', index_col=0, skiprows=2)
benchmark.index = pd.to_datetime(benchmark.index, format='%Y-%m-%d', utc=True)

display(positions)
AMD CERN COST DELL GPS INTC MMM cash
index
2004-01-09 00:00:00+00:00 6961.92 21017.078750 7282.266152 21264.55188 7091.080020 21259.333890 21316.129606 -6192.360298
2004-01-12 00:00:00+00:00 18198.58 18071.250000 17675.836401 10804.31924 10685.411865 17872.477480 10882.026400 -3329.289887
2004-01-13 00:00:00+00:00 12060.86 11942.246250 12838.477446 16078.90380 16272.139000 12465.392511 12579.135758 4708.039735
2004-01-14 00:00:00+00:00 13102.40 15534.281250 14447.422640 15414.45080 15666.440185 14884.069620 13454.542620 -2749.470030
2004-01-15 00:00:00+00:00 15518.40 14547.050000 14164.039680 14407.48813 14926.122619 14422.385864 13929.159049 -2462.919316
... ... ... ... ... ... ... ... ...
2009-12-24 00:00:00+00:00 -1199.11 1316.857500 22778.660580 -3562.47039 76601.638113 36280.269375 17740.890304 16350.679211
2009-12-28 00:00:00+00:00 589.80 673.840032 24170.422856 -1765.41500 83143.517604 37499.607147 15692.520137 7641.201795
2009-12-29 00:00:00+00:00 292.50 334.920016 20993.396552 858.85252 94500.729990 50509.461877 7946.648597 -6583.290764
2009-12-30 00:00:00+00:00 1681.56 -167.179992 34934.764512 91207.82625 29751.588246 38052.304640 -3926.109096 -22234.787956
2009-12-31 00:00:00+00:00 22254.32 9975.240484 47781.667800 53022.51955 27393.148240 18850.582240 -1934.275491 -11938.952799

1506 rows × 8 columns

Pyfolio

Tear Sheet

returns = positions.sum(axis=1).pct_change().dropna()
benchmark_rets = benchmark.iloc[:,0].pct_change().dropna()
benchmark_rets.rename('SP500', inplace=True)

pf.create_full_tear_sheet(returns, benchmark_rets=benchmark_rets)
Start date 2004-01-12
End date 2009-12-31
Total months 71
Backtest
Annual return 8.791%
Cumulative returns 65.404%
Annual volatility 26.26%
Sharpe ratio 0.45
Calmar ratio 0.15
Stability 0.00
Max drawdown -60.391%
Omega ratio 1.09
Sortino ratio 0.66
Skew 0.14
Kurtosis 5.88
Tail ratio 0.99
Daily value at risk -3.261%
Alpha 0.10
Beta 0.84
Worst drawdown periods Net drawdown in % Peak date Valley date Recovery date Duration
0 60.39 2007-11-06 2009-03-09 NaT NaN
1 24.10 2005-07-28 2006-09-07 2007-05-22 474
2 11.89 2004-06-25 2004-08-12 2004-11-05 96
3 10.87 2004-11-15 2005-04-18 2005-07-14 174
4 9.51 2007-07-16 2007-08-06 2007-09-13 44
Stress Events mean min max
Lehman -0.27% -4.70% 4.11%
Aug07 0.32% -2.96% 2.92%
Mar08 -0.43% -3.26% 3.36%
Sept08 -0.68% -4.38% 4.08%
2009Q1 -0.36% -5.02% 3.39%
2009Q2 0.74% -4.03% 6.13%
Low Volatility Bull Market 0.01% -6.07% 6.43%
GFC Crash -0.09% -11.76% 10.13%
Recovery 0.35% -4.03% 6.01%

Pyfolio

Position Tear Sheet

# Mapowanie sektorów
sect_map = {'COST': 'Consumer Goods',
            'INTC': 'Technology', 
            'CERN': 'Healthcare', 
            'GPS': 'Technology',
            'MMM': 'Construction', 
            'DELL': 'Technology', 
            'AMD': 'Technology'}

pf.create_position_tear_sheet(returns,positions, sector_mappings=sect_map)
Top 10 long positions of all time max
COST 90.01%
DELL 85.73%
CERN 83.53%
MMM 82.09%
INTC 78.59%
AMD 75.76%
GPS 62.24%
Top 10 short positions of all time max
AMD -30.12%
DELL -26.58%
CERN -25.51%
MMM -22.62%
GPS -20.09%
INTC -18.47%
COST -16.44%
Top 10 positions of all time max
COST 90.01%
DELL 85.73%
CERN 83.53%
MMM 82.09%
INTC 78.59%
AMD 75.76%
GPS 62.24%

Teoria portfelowa

…po angielsku modern portfolio theory lub mean–variance analysis.

Główna idea – Efektywna granica

…czyli Efficient frontier.

To zbiór porfeli, dla których nie istnieje inny portfel o wyższym oczekiwanym zwrocie przy tym samym odchyleniu standardowym zwrotu (ryzyku).

Szukamy takich wag, które pozwolą nam zminimalizować wariancję

\[ \min_{\mathbf{w}} = \mathbf{w}^T\Sigma\mathbf{w}. \]

Dla dobranych wag wartość oczekiwana zwrotu powinna odpowiadać ustalonemu poziomowi

\[ \mathbf{w}^T\mathbf{\mu} = \mu_p. \]

Wagi muszą sumować się do 1

\[ \mathbf{w}^T\mathbf{1}=1. \]

Graficzna reprezentacja

Źródło: https://inyova.ch/en/expertise/efficient-frontier-investment-theory/

Biblioteka PyortfolioOpt

Jak wyglądają nasze dane?

AAPL GE AMD WMT BAC T XOM BBY PFE JPM
date
1989-12-29 0.117203 0.352438 3.9375 3.486070 1.752478 2.365775 1.766756 0.166287 0.110818 1.827968
1990-01-02 0.123853 0.364733 4.1250 3.660858 1.766686 2.398184 1.766756 0.173216 0.113209 1.835617
1990-01-03 0.124684 0.364050 4.0000 3.660858 1.780897 2.356516 1.749088 0.194001 0.113608 1.896803

Importujemy potrzebne funkcje

from pypfopt import expected_returns
from pypfopt import risk_models

Liczymy średnie zwroty dla danych…

mu = expected_returns.mean_historical_return(stock_prices)

Pierwsze 5 wyników dla średnich zwrotów

AAPL    0.294305
GE      0.136009
AMD     0.032850
WMT     0.120012
BAC     0.105540
dtype: float64

Liczymy macierz kowariancji zwrotów dla danych…

Sigma = risk_models.sample_cov(stock_prices)

Pierwsze 5 wyników dla macierzy kowariancji

          AAPL        GE       AMD       WMT       BAC
AAPL  0.207537  0.038348  0.084594  0.026599  0.046262
GE    0.038348  0.083109  0.048893  0.029263  0.063275
AMD   0.084594  0.048893  0.390586  0.027288  0.068712
WMT   0.026599  0.029263  0.027288  0.069092  0.030757
BAC   0.046262  0.063275  0.068712  0.030757  0.180192

Wybór portfela

from pypfopt.efficient_frontier import EfficientFrontier

Liczymy Efficient frontier…

ef_return = EfficientFrontier(mu, Sigma)
ef_sharpe = EfficientFrontier(mu, Sigma)
ef_vol = EfficientFrontier(mu, Sigma)


Wybieramy porfel o rocznej stopie zwrotu 20%…

ef_return.efficient_return(0.2)

Wagi

ef_return.clean_weights()
AAPL: 0.16759
GE: 0.00000
AMD: 0.00000
WMT: 0.08029
BAC: 0.00000
T: 0.05185
XOM: 0.28483
BBY: 0.07526
PFE: 0.34018
JPM: 0.00000

Statystyki

ef_return.portfolio_performance(verbose=True)
Expected annual return: 20.0%
Annual volatility: 19.9%
Sharpe Ratio: 1.01
(0.1999999999999999, 0.19898336191007587, 1.0051091612894925)


Wybieramy porfel o najwyższym wskaźniku Sharpe’a…

ef_sharpe.max_sharpe()

Wagi

AAPL: 0.20430
GE: 0.00000
AMD: 0.00000
WMT: 0.03511
BAC: 0.00000
T: 0.00000
XOM: 0.26966
BBY: 0.09112
PFE: 0.39980
JPM: 0.00000

Statystyki

Expected annual return: 21.5%
Annual volatility: 21.3%
Sharpe Ratio: 1.01
(0.21532248096661105, 0.2127665321341155, 1.0120129270654485)


Wybieramy porfel o najmniejszej zmienności (odchyleniu standardowym zwrotów)…

ef_vol.min_volatility()

Wagi

AAPL: 0.04306
GE: 0.05511
AMD: 0.00000
WMT: 0.21254
BAC: 0.00000
T: 0.21768
XOM: 0.31582
BBY: 0.01963
PFE: 0.13616
JPM: 0.00000

Statystyki

Expected annual return: 14.8%
Annual volatility: 17.7%
Sharpe Ratio: 0.84
(0.14823493562996323, 0.17710888575470818, 0.8369706296682669)

Alternatywne sposoby obliczania średniej i wariancji

Zwroty i ryzyko ważone wykładniczo

Nadajemy większe wagi nowym danym.

mu_exp = expected_returns.ema_historical_return(stock_prices, span=180, 
  frequency=252)
Sigma_exp = risk_models.exp_cov(stock_prices, span=180, frequency=252)
ef_exp = EfficientFrontier(mu_exp, Sigma_exp)
ef_exp.max_sharpe()

Statystyki

Expected annual return: 35.8%
Annual volatility: 23.3%
Sharpe Ratio: 1.54
(0.357758457877451, 0.23304517663697047, 1.5351463739357045)


Semikowariancja

… czyli wariancja ze zwrotów poniżej ustalonego benchmarku.

semi_Sigma = risk_models.semicovariance(stock_prices, 
  benchmark=0, frequency=252)

Pierwsze 5 wyników dla macierzy semikowariancji

          AAPL        GE       AMD       WMT       BAC
AAPL  0.092904  0.025240  0.059902  0.021780  0.033612
GE    0.025240  0.036607  0.037300  0.017666  0.034130
AMD   0.059902  0.037300  0.178330  0.027033  0.052319
WMT   0.021780  0.017666  0.027033  0.031344  0.021281
BAC   0.033612  0.034130  0.052319  0.021281  0.080705

Podsumowanie

  • Podstawowe pojęcia: Portfel, Dywersyfikacja, Zwrot, Wariancja
  • Miary ryzyka: Skośność, Kurtoza, Współczynnik Sharpe’a
  • Analiza wyników: Aktywne Wagi, Fama French, Pyfolio
  • Teoria portfelowa: Efektywna granica, Wybór portfela

Źródła